package com.kk.netty.nio.network.selector;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import static com.kk.netty.nio.ByteBufferUtil.debugAll;

@Slf4j
public class BoundaryService {
    // sscKey 、 scKey 一人一个管理员
    public static void main(String[] args) throws IOException {
        // 1、创建 selector 管理多个 channel
        Selector selector = Selector.open ( );
        ServerSocketChannel ssc = ServerSocketChannel.open ( );
        ssc.configureBlocking (false);

        // 2、建立 selector 和 channel 的联系(注册)
        ssc.register (selector, SelectionKey.OP_ACCEPT);
        // SelectionKey 当事件发生后，通过它得到事件类型 和 哪个 channel 发生了该事件
        SelectionKey sscKey = ssc.register (selector, 0, null);

        // key 只关注 accept 事件
        // interestOps 只关注
        sscKey.interestOps (SelectionKey.OP_ACCEPT);
        log.debug ("register key:{}", sscKey);
        ssc.bind (new InetSocketAddress (8080));

        while (true) {
            // 3、select 方法，没有事件发生，线程阻塞；有事件，才恢复运行
            //select 在事件未处理的时候，它不会阻塞；事件发生后，要么处理或者取消，不能置之不理
            selector.select ( );

            // 4、处理事件，selectedKey 内部包含了所有发生的事件
            Set<SelectionKey> keys = selector.selectedKeys ( );
            // 遍历所有事件，逐一处理
            Iterator<SelectionKey> iter = keys.iterator ( );
            while (iter.hasNext ( )) {
                SelectionKey key = iter.next ( );
                iter.remove ( );
                log.debug ("key:{}", key);

                // 5、区分事件类型
                if (key.isAcceptable ( )) {// 如果是 accept
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel ( );
                    SocketChannel sc = channel.accept ( );
                    sc.configureBlocking (false);
                    ByteBuffer buffer = ByteBuffer.allocate (16);// attachment
                    // 将一个 byteBuffer 作为附件关联到 selectionKey 上
                    SelectionKey scKey = sc.register (selector, 0, buffer);
                    scKey.interestOps (SelectionKey.OP_READ);
                } else if (key.isReadable ( )) {// 如果是 read
                    try {
                        // 拿到触发的事件 channel
                        SocketChannel channel = (SocketChannel) key.channel ( );
                        // 获取 selectionKey 上关联的事件
                        //ByteBuffer buffer = ByteBuffer.allocate (16);
                        ByteBuffer buffer = (ByteBuffer) key.attachment ( );
                        // 如果是正常断开。read 的方法返回值是 -1
                        int read = channel.read (buffer);
                        if (read == -1) {
                            key.cancel ( );
                        } else {
                            // 压缩读取一次，确保数据完整
                            split (buffer);
                            // 如果游标的起始相等，说明容量已满，需要扩容
                            if (buffer.position ( ) == buffer.limit ( )) {
                                // 扩容两倍
                                ByteBuffer newBuffer = ByteBuffer.allocate (buffer.capacity ( ) * 2);
                                buffer.flip ( );
                                newBuffer.put (buffer);
                                key.attach (newBuffer);
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace ( );
                        // 因为客户端断开了，因此需要将 key 取消（从 selector 得keys 集合中真正删除）
                        key.cancel ( );
                    }
                }

            }
        }

    }


    // 向前压缩一条完整数据
    private static void split(ByteBuffer source) {
        source.flip ( );
        for (int i = 0; i < source.limit ( ); i++) {
            // 找到一条完整消息
            if (source.get (i) == '\n') {
                int length = i + 1 - source.position ( );
                // 把这条完整消息存入新的 ByteBuffer
                ByteBuffer target = ByteBuffer.allocate (length);
                // 从 source 读，向 target 写
                for (int j = 0; j < length; j++) {
                    target.put (source.get ( ));
                }
                debugAll (target);
            }
        }
        source.compact ( ); // 0123456789abcdef  position 16 limit 16
    }

    // 未处理边界的代码
    public void unprocessedBoundary() throws Exception {
        ServerSocket ss = new ServerSocket (9000);
        while (true) {
            Socket socket = ss.accept ( );
            InputStream in = socket.getInputStream ( );
            // 这里这么写，有没有问题
            byte[] arr = new byte[4];
            while (true) {
                int read = in.read (arr);
                // 这里这么写，有没有问题
                if (read == -1) {
                    break;
                }
                System.out.println (new String (arr, 0, read));
            }
        }
    }
}
